/**
 * JS对象
 */
interface IObject {
  [key: string]: string | number;
}

/**
 * 允许转换的视口单位
 */
type IUnit = 'vw' | 'vmin' | 'vmax';

/**
 * 参数配置
 */
interface IOptions {
  /**
   * 设计稿宽度
   * @default 750
   */
  viewportWidth: number;
  /**
   * 转换后的视口单位
   * @default "vw"
   */
  viewportUnit: IUnit;
  /**
   * 转换后的字体视口单位
   * @default "vw"
   */
  fontViewportUnit: IUnit;
  /**
   * 单位精度
   * @default 10
   */
  unitPrecision: number;
}

/**
 * 可选参数配置
 */
type IPartialOptions = Partial<IOptions>;

/**
 * 精准小数点
 */
type IToFixed = (value: number, precision: number) => number;

/**
 * 正则单位转换
 */
type ICreateRpxReplace = (
  opts: IOptions,
  viewportUnit: IOptions['viewportUnit'],
  viewportSize: IOptions['viewportWidth'],
) => (m: string, $1: string) => string;

/**
 * 获取对应单位
 */
type IGetUnit = (prop: string, opts: IOptions) => IUnit;

/**
 * 初始化配置
 */
type IInitUnit = (options?: IPartialOptions) => void;

/**
 * 单位转换
 */
type ITransform = (value: number) => number;

/**
 * 书写行内样式
 */
type IInlineStyles = (styles: IObject, options?: IPartialOptions) => IObject;

/**
 * 精准小数点
 */
const toFixed: IToFixed = (value, precision) => {
  return Number(value.toFixed(precision));
};

/**
 * 正则单位转换
 */
const createRpxReplace: ICreateRpxReplace = (opts, viewportUnit, viewportSize) => {
  return (m, $1) => {
    if (!$1) return m;
    const pixels = parseFloat($1);
    const parsedVal = toFixed((pixels / viewportSize) * 100, opts.unitPrecision);
    return parsedVal + viewportUnit;
  };
};

/**
 * 获取对应单位
 */
const getUnit: IGetUnit = (prop, opts) => {
  const { viewportUnit, fontViewportUnit } = opts;
  return prop.indexOf('font') === -1 ? viewportUnit : fontViewportUnit;
};

/**
 * 正则匹配单位
 */
const rpxRegex = /"[^"]+"|'[^']+'|url\([^\)]+\)|(\d*\.?\d+)rpx/g;

/**
 * 默认配置
 */
const defaultOptions: IOptions = {
  viewportWidth: 750,
  viewportUnit: 'vw',
  fontViewportUnit: 'vw',
  unitPrecision: 10,
};

/**
 * 初始化配置
 */
let initedOptions = {} as IPartialOptions;

/**
 * 初始化配置
 */
export const initUnit: IInitUnit = (options) => {
  initedOptions = Object.assign({}, options);
};

/**
 * 单位转换：px to rpx
 */
export const px2rpx: ITransform = (value) => {
  const opts = Object.assign({}, defaultOptions, initedOptions);
  const { viewportWidth, unitPrecision } = opts;
  return toFixed((viewportWidth * value) / innerWidth, unitPrecision);
};

/**
 * 单位转换：rpx to px
 */
export const rpx2px: ITransform = (value) => {
  const opts = Object.assign({}, defaultOptions, initedOptions);
  const { viewportWidth, unitPrecision } = opts;
  return toFixed((innerWidth / viewportWidth) * value, unitPrecision);
};

/**
 * 书写行内样式
 */
export const inlineStyles: IInlineStyles = (styles, options) => {
  const shallowCopyStyles = Object.assign({}, styles);
  const opts = Object.assign({}, defaultOptions, initedOptions, options);
  for (const prop in shallowCopyStyles) {
    const value = shallowCopyStyles[prop];
    if (
      Object.hasOwnProperty.call(shallowCopyStyles, prop) &&
      typeof value === 'string' &&
      value.indexOf('rpx') !== -1
    ) {
      const unit = getUnit(prop, opts);
      shallowCopyStyles[prop] = value.replace(rpxRegex, createRpxReplace(opts, unit, opts.viewportWidth));
    }
  }
  return shallowCopyStyles;
};

export default {
  /**
   * 初始化配置
   */
  initUnit,
  /**
   * 单位转换：px to rpx
   */
  px2rpx,
  /**
   * 单位转换：rpx to px
   */
  rpx2px,
  /**
   * 书写行内样式
   */
  inlineStyles,
};
